// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package fuzz

import (
	"fmt"
	"os"
	"os/exec"
	"reflect"
	"syscall"
	"unsafe"
)

type sharedMemSys struct {
	mapObj syscall.Handle
}

func sharedMemMapFile(f *os.File, size int, removeOnClose bool) (mem *sharedMem, err error) {
	defer func() {
		if err != nil {
			err = fmt.Errorf("mapping temporary file %s: %w", f.Name(), err)
		}
	}()

	// 创建一个文件映射对象。对象本身不是共享的。
	mapObj, err := syscall.CreateFileMapping(
		syscall.Handle(f.Fd()), // F手柄
		nil,                    // sa 
		syscall.PAGE_READWRITE, // prot 
		0,                      // maxSizeHigh 
		0,                      // maxSizeLow 
		nil,                    // name 
	)
	if err != nil {
		return nil, err
	}

	// 从文件映射对象创建视图。
	access := uint32(syscall.FILE_MAP_READ | syscall.FILE_MAP_WRITE)
	addr, err := syscall.MapViewOfFile(
		mapObj,        // handle 
		access,        // access 
		0,             // offsetHigh 
		0,             // offsetLow 
		uintptr(size), // length 
	)
	if err != nil {
		syscall.CloseHandle(mapObj)
		return nil, err
	}

	var region []byte
	header := (*reflect.SliceHeader)(unsafe.Pointer(&region))
	header.Data = addr
	header.Len = size
	header.Cap = size
	return &sharedMem{
		f:             f,
		region:        region,
		removeOnClose: removeOnClose,
		sys:           sharedMemSys{mapObj: mapObj},
	}, nil
}

// Close取消映射共享内存并关闭临时文件。如果此
// sharedMem是使用sharedMemTempFile创建的，则Close还会删除该文件。
func (m *sharedMem) Close() error {
	// 尝试所有操作，即使之前的操作出现错误。
	// os。文件Close可能会因为I/O错误而失败，但我们仍然希望删除临时文件
	// 。
	var errs []error
	errs = append(errs,
		syscall.UnmapViewOfFile(uintptr(unsafe.Pointer(&m.region[0]))),
		syscall.CloseHandle(m.sys.mapObj),
		m.f.Close())
	if m.removeOnClose {
		errs = append(errs, os.Remove(m.f.Name()))
	}
	for _, err := range errs {
		if err != nil {
			return err
		}
	}
	return nil
}

// setWorkerComm在cmd上配置通信通道，以运行工作进程。
func setWorkerComm(cmd *exec.Cmd, comm workerComm) {
	mem := <-comm.memMu
	memName := mem.f.Name()
	comm.memMu <- mem
	syscall.SetHandleInformation(syscall.Handle(comm.fuzzIn.Fd()), syscall.HANDLE_FLAG_INHERIT, 1)
	syscall.SetHandleInformation(syscall.Handle(comm.fuzzOut.Fd()), syscall.HANDLE_FLAG_INHERIT, 1)
	cmd.Env = append(cmd.Env, fmt.Sprintf("GO_TEST_FUZZ_WORKER_HANDLES=%x,%x,%q", comm.fuzzIn.Fd(), comm.fuzzOut.Fd(), memName))
	cmd.SysProcAttr = &syscall.SysProcAttr{AdditionalInheritedHandles: []syscall.Handle{syscall.Handle(comm.fuzzIn.Fd()), syscall.Handle(comm.fuzzOut.Fd())}}
}

// GetWorkerCommand返回工作进程中的通信通道。
func getWorkerComm() (comm workerComm, err error) {
	v := os.Getenv("GO_TEST_FUZZ_WORKER_HANDLES")
	if v == "" {
		return workerComm{}, fmt.Errorf("GO_TEST_FUZZ_WORKER_HANDLES not set")
	}
	var fuzzInFD, fuzzOutFD uintptr
	var memName string
	if _, err := fmt.Sscanf(v, "%x,%x,%q", &fuzzInFD, &fuzzOutFD, &memName); err != nil {
		return workerComm{}, fmt.Errorf("parsing GO_TEST_FUZZ_WORKER_HANDLES=%s: %v", v, err)
	}

	fuzzIn := os.NewFile(fuzzInFD, "fuzz_in")
	fuzzOut := os.NewFile(fuzzOutFD, "fuzz_out")
	tmpFile, err := os.OpenFile(memName, os.O_RDWR, 0)
	if err != nil {
		return workerComm{}, fmt.Errorf("worker opening temp file: %w", err)
	}
	fi, err := tmpFile.Stat()
	if err != nil {
		return workerComm{}, fmt.Errorf("worker checking temp file size: %w", err)
	}
	size := int(fi.Size())
	if int64(size) != fi.Size() {
		return workerComm{}, fmt.Errorf("fuzz temp file exceeds maximum size")
	}
	removeOnClose := false
	mem, err := sharedMemMapFile(tmpFile, size, removeOnClose)
	if err != nil {
		return workerComm{}, err
	}
	memMu := make(chan *sharedMem, 1)
	memMu <- mem

	return workerComm{fuzzIn: fuzzIn, fuzzOut: fuzzOut, memMu: memMu}, nil
}

func isInterruptError(err error) bool {
	// 在Windows上，我们无法判断进程是否被Wait返回的错误
	// 中断。它看起来像是一个状态为1的恐怖分子。
	return false
}

// terminationSignal返回-1和false，因为Windows没有信号。
func terminationSignal(err error) (os.Signal, bool) {
	return syscall.Signal(-1), false
}

// isCrashSignal未实现，因为Windows没有信号。
func isCrashSignal(signal os.Signal) bool {
	panic("not implemented: no signals on windows")
}
